home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / shllutil.lha / shellutils-1.8 / src / pathchk.c < prev    next >
C/C++ Source or Header  |  1992-09-10  |  9KB  |  333 lines

  1. /* pathchk -- check whether pathnames are valid or portable
  2.    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: pathchk [-p] [--portability] path...
  19.  
  20.    For each PATH, print a message if any of these conditions are false:
  21.    * all existing leading directories in PATH have search (execute) permission
  22.    * strlen (PATH) <= PATH_MAX
  23.    * strlen (each_directory_in_PATH) <= NAME_MAX
  24.  
  25.    Exit status:
  26.    0            All PATH names passed all of the tests.
  27.    1            An error occurred.
  28.  
  29.    Options:
  30.    -p, --portability    Instead of performing length checks on the
  31.             underlying filesystem, test the length of the
  32.             pathname and its components against the POSIX.1
  33.             minimum limits for portability, _POSIX_NAME_MAX
  34.             and _POSIX_PATH_MAX in 2.9.2.  Also check that
  35.             the pathname contains no characters not in the
  36.             portable filename character set.
  37.  
  38.    David MacKenzie <djm@gnu.ai.mit.edu>
  39.    and Jim Meyering <meyering@cs.utexas.edu> */
  40.  
  41. #include <stdio.h>
  42. #include <getopt.h>
  43. #include <sys/types.h>
  44. #include "system.h"
  45.  
  46. #ifdef _POSIX_VERSION
  47. #include <limits.h>
  48. #ifndef PATH_MAX
  49. #define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
  50. #endif /* not PATH_MAX */
  51. #ifndef NAME_MAX
  52. #define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
  53. #endif /* not NAME_MAX */
  54.  
  55. #else /* not _POSIX_VERSION */
  56.  
  57. #include <sys/param.h>
  58. #ifndef PATH_MAX
  59. #ifdef MAXPATHLEN
  60. #define PATH_MAX MAXPATHLEN
  61. #else /* not MAXPATHLEN */
  62. #define PATH_MAX _POSIX_PATH_MAX
  63. #endif /* not MAXPATHLEN */
  64. #endif /* not PATH_MAX */
  65.  
  66. #ifndef NAME_MAX
  67. #ifdef MAXNAMLEN
  68. #define NAME_MAX MAXNAMLEN
  69. #else /* not MAXNAMLEN */
  70. #define NAME_MAX _POSIX_NAME_MAX
  71. #endif /* not MAXNAMLEN */
  72. #endif /* not NAME_MAX */
  73.  
  74. #endif /* not _POSIX_VERSION */
  75.  
  76. #ifndef _POSIX_PATH_MAX
  77. #define _POSIX_PATH_MAX 255
  78. #endif
  79. #ifndef _POSIX_NAME_MAX
  80. #define _POSIX_NAME_MAX 14
  81. #endif
  82.  
  83. #ifndef PATH_MAX_FOR
  84. #define PATH_MAX_FOR(p) PATH_MAX
  85. #endif
  86. #ifndef NAME_MAX_FOR
  87. #define NAME_MAX_FOR(p) NAME_MAX
  88. #endif
  89.  
  90. char *xstrdup();
  91. int validate_path ();
  92. void error ();
  93. void usage ();
  94.  
  95. /* The name this program was run with. */
  96. char *program_name;
  97.  
  98. struct option longopts[] =
  99. {
  100.   {"portability", 0, NULL, 'p'},
  101.   {NULL, 0, NULL, 0}
  102. };
  103.  
  104. void
  105. main (argc, argv)
  106.      int argc;
  107.      char **argv;
  108. {
  109.   int exit_status = 0;
  110.   int check_portability = 0;
  111.   int optc;
  112.  
  113.   program_name = argv[0];
  114.  
  115.   while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
  116.     {
  117.       switch (optc)
  118.     {
  119.     case 'p':
  120.       check_portability = 1;
  121.       break;
  122.     default:
  123.       usage ();
  124.     }
  125.     }
  126.  
  127.   if (optind == argc)
  128.     usage ();
  129.  
  130.   for (; optind < argc; ++optind)
  131.     exit_status |= validate_path (argv[optind], check_portability);
  132.  
  133.   exit (exit_status);
  134. }
  135.  
  136. /* Each element is nonzero if the corresponding ASCII character is
  137.    in the POSIX portable character set, and zero if it is not.
  138.    In addition, the entry for `/' is nonzero to simplify checking. */
  139. char portable_chars[] =
  140. {
  141.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
  142.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
  143.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
  144.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
  145.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
  146.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
  147.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
  148.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
  149.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  150.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  151.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  152.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  153.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  154.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  155.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  156.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  157. };
  158.  
  159. /* If PATH contains only portable characters, return 1, else 0.  */
  160.  
  161. int
  162. portable_chars_only (path)
  163.      char *path;
  164. {
  165.   char *p;
  166.  
  167.   for (p = path; *p; ++p)
  168.     if (portable_chars[*p] == 0)
  169.       {
  170.     error (0, 0, "path `%s' contains nonportable character `%c'",
  171.            path, *p);
  172.     return 0;
  173.       }
  174.   return 1;
  175. }
  176.  
  177. /* Return 1 if PATH is a usable leading directory, 0 if not,
  178.    2 if it doesn't exist.  */
  179.  
  180. int
  181. dir_ok (path)
  182.      char *path;
  183. {
  184.   struct stat stats;
  185.  
  186.   if (stat (path, &stats))
  187.     return 2;
  188.  
  189.   if (!S_ISDIR (stats.st_mode))
  190.     {
  191.       error (0, 0, "`%s' is not a directory", path);
  192.       return 0;
  193.     }
  194.  
  195.   /* Use access to test for search permission because
  196.      testing permission bits of st_mode can lose with new
  197.      access control mechanisms.  Of course, access loses if you're
  198.      running setuid. */
  199.   if (access (path, X_OK) != 0)
  200.     {
  201.       if (errno == EACCES)
  202.     error (0, 0, "directory `%s' is not searchable", path);
  203.       else
  204.     error (0, errno, "%s", path);
  205.       return 0;
  206.     }
  207.  
  208.   return 1;
  209. }
  210.  
  211. /* Make sure that
  212.    strlen (PATH) <= PATH_MAX
  213.    && strlen (each-existing-directory-in-PATH) <= NAME_MAX
  214.  
  215.    If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
  216.    _POSIX_NAME_MAX instead, and make sure that PATH contains no
  217.    characters not in the POSIX portable filename character set, which
  218.    consists of A-Z, a-z, 0-9, ., _, -.
  219.  
  220.    Make sure that all leading directories along PATH that exist have
  221.    `x' permission.
  222.  
  223.    Return 0 if all of these tests are successful, 1 if any fail. */
  224.  
  225. int
  226. validate_path (path, portability)
  227.      char *path;
  228.      int portability;
  229. {
  230.   int path_max;
  231.   int last_elem;        /* Nonzero if checking last element of path. */
  232.   int exists;            /* 2 if the path element exists.  */
  233.   char *slash;
  234.   char *parent;            /* Last existing leading directory so far.  */
  235.  
  236.   if (portability && !portable_chars_only (path))
  237.     return 1;
  238.  
  239.   if (*path == '\0')
  240.     return 0;
  241.  
  242.   /* Figure out the parent of the first element in PATH.  */
  243.   parent = xstrdup (*path == '/' ? "/" : ".");
  244.  
  245.   slash = path;
  246.   last_elem = 0;
  247.   while (1)
  248.     {
  249.       int name_max;
  250.       int length;        /* Length of partial path being checked. */
  251.       char *start;        /* Start of path element being checked. */
  252.  
  253.       /* Find the end of this element of the path.
  254.      Then chop off the rest of the path after this element. */
  255.       while (*slash == '/')
  256.     slash++;
  257.       start = slash;
  258.       slash = index (slash, '/');
  259.       if (slash != NULL)
  260.     *slash = '\0';
  261.       else
  262.     {
  263.       last_elem = 1;
  264.       slash = index (start, '\0');
  265.     }
  266.  
  267.       if (!last_elem)
  268.     {
  269.       exists = dir_ok (path);
  270.       if (dir_ok == 0)
  271.         {
  272.           free (parent);
  273.           return 1;
  274.         }
  275.     }
  276.  
  277.       length = slash - start;
  278.       /* Since we know that `parent' is a directory, it's ok to call
  279.      pathconf with it as the argument.  (If `parent' isn't a directory
  280.      or doesn't exist, the behavior of pathconf is undefined.)
  281.      But if `parent' is a directory and is on a remote file system,
  282.      it's likely that pathconf can't give us a reasonable value
  283.      and will return -1.  (NFS and tempfs are not POSIX . . .)
  284.      In that case, we have no choice but to assume the pessimal
  285.      POSIX minimums.  */
  286.       name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
  287.       if (name_max < 0)
  288.     name_max = _POSIX_NAME_MAX;
  289.       if (length > name_max)
  290.     {
  291.       error (0, 0, "name `%s' has length %d; exceeds limit of %d",
  292.          start, length, name_max);
  293.       free (parent);
  294.       return 1;
  295.     }
  296.  
  297.       if (last_elem)
  298.     break;
  299.  
  300.       if (exists == 1)
  301.     {
  302.       free (parent);
  303.       parent = xstrdup (path);
  304.     }
  305.  
  306.       *slash++ = '/';
  307.     }
  308.  
  309.   /* `parent' is now the last existing leading directory in the whole path,
  310.      so it's ok to call pathconf with it as the argument.  */
  311.   path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
  312.   if (path_max < 0)
  313.     path_max = _POSIX_PATH_MAX;
  314.   free (parent);
  315.   if (strlen (path) > path_max)
  316.     {
  317.       error (0, 0, "path `%s' has length %d; exceeds limit of %d",
  318.          path, strlen (path), path_max);
  319.       return 1;
  320.     }
  321.  
  322.   return 0;
  323. }
  324.  
  325. void
  326. usage ()
  327. {
  328.   fprintf (stderr, "\
  329. Usage: %s [-p] [--portability] path...\n",
  330.        program_name);
  331.   exit (1);
  332. }
  333.